<FrameworkSwitchCourse {fw} />

# Sumarizare[[summarization]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={7}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter7/section5_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section5_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={7}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter7/section5_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section5_tf.ipynb"},
]} />

{/if}


În această secțiune vom analiza modul în care modelele Transformer pot fi utilizate pentru a condensa documente lungi în rezumate, o sarcină cunoscută sub numele de _text summarization_. Aceasta este una dintre cele mai dificile sarcini NLP, deoarece necesită o gamă largă de abilități, cum ar fi înțelegerea pasajelor lungi și generarea unui text coerent care integrează principalele subiecte dintr-un document. Cu toate acestea, atunci când este bine realizată, rezumarea textului este un instrument puternic care poate accelera diverse procese de business prin scutirea experților într-un anumit domeniu de a citi documente lungi în detaliu.

<Youtube id="yHnr5Dk2zCI"/>

Deși există deja diverse modele bine puse la punct pentru sumarizare pe [Hugging Face Hub] (https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), aproape toate acestea sunt potrivite numai pentru documentele în limba engleză. Prin urmare, pentru a adăuga o întorsătură în această secțiune, vom antrena un model bilingv pentru engleză și spaniolă. Până la sfârșitul acestei secțiuni, veți avea un [model](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) care poate rezuma recenziile clienților precum cel prezentat aici:


<iframe src="https://course-demos-mt5-small-finetuned-amazon-en-es.hf.space" frameBorder="0" height="400" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

După cum vom vedea, aceste rezumate sunt concise deoarece sunt învățate din titlurile pe care clienții le furnizează în recenziile lor despre produse. Să începem prin alcătuirea unui corpus bilingv adecvat pentru această sarcină.

## Pregătirea unui corpus multilingv[[preparing-a-multilingual-corpus]]

Vom utiliza [Multilingual Amazon Reviews Corpus] (https://huggingface.co/datasets/amazon_reviews_multi) pentru a crea bilingv summarizerul nostru. Acest corpus este format din recenzii ale produselor Amazon în șase limbi și este utilizat de obicei pentru a evalua clasificatoarele multilingve. Cu toate acestea, deoarece fiecare recenzie este însoțită de un titlu scurt, putem folosi titlurile ca rezumate țintă din care modelul nostru să învețe! Pentru a începe, să descărcăm subseturile în engleză și spaniolă de la Hugging Face Hub:

```python
from datasets import load_dataset

spanish_dataset = load_dataset("amazon_reviews_multi", "es")
english_dataset = load_dataset("amazon_reviews_multi", "en")
english_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 200000
    })
    validation: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 5000
    })
    test: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 5000
    })
})
```

După cum puteți vedea, pentru fiecare limbă există 200.000 de recenzii pentru splitul `train` și 5.000 de recenzii pentru fiecare dintre spliturile `validation` și `test`. Informațiile despre recenzii care ne interesează sunt conținute în coloanele `review_body` și `review_title`. Să analizăm câteva exemple prin crearea unei funcții simple care preia un sample aleatoriu din setul de antrenare cu ajutorul tehnicilor învățate în [Capitolul 5](/course/chapter5):

```python
def show_samples(dataset, num_samples=3, seed=42):
    sample = dataset["train"].shuffle(seed=seed).select(range(num_samples))
    for example in sample:
        print(f"\n'>> Title: {example['review_title']}'")
        print(f"'>> Review: {example['review_body']}'")


show_samples(english_dataset)
```

```python out
'>> Title: Worked in front position, not rear'
'>> Review: 3 stars because these are not rear brakes as stated in the item description. At least the mount adapter only worked on the front fork of the bike that I got it for.'

'>> Title: meh'
'>> Review: Does it’s job and it’s gorgeous but mine is falling apart, I had to basically put it together again with hot glue'

'>> Title: Can\'t beat these for the money'
'>> Review: Bought this for handling miscellaneous aircraft parts and hanger "stuff" that I needed to organize; it really fit the bill. The unit arrived quickly, was well packaged and arrived intact (always a good sign). There are five wall mounts-- three on the top and two on the bottom. I wanted to mount it on the wall, so all I had to do was to remove the top two layers of plastic drawers, as well as the bottom corner drawers, place it when I wanted and mark it; I then used some of the new plastic screw in wall anchors (the 50 pound variety) and it easily mounted to the wall. Some have remarked that they wanted dividers for the drawers, and that they made those. Good idea. My application was that I needed something that I can see the contents at about eye level, so I wanted the fuller-sized drawers. I also like that these are the new plastic that doesn\'t get brittle and split like my older plastic drawers did. I like the all-plastic construction. It\'s heavy duty enough to hold metal parts, but being made of plastic it\'s not as heavy as a metal frame, so you can easily mount it to the wall and still load it up with heavy stuff, or light stuff. No problem there. For the money, you can\'t beat it. Best one of these I\'ve bought to date-- and I\'ve been using some version of these for over forty years.'
```

> [!TIP]
> ✏️ **Încercați!** Schimbați seedul aleatoriu în comanda `Dataset.shuffle()` pentru a explora alte recenzii din corpus. Dacă sunteți vorbitor de spaniolă, aruncați o privire la unele dintre recenziile din `spanish_dataset` pentru a vedea dacă și titlurile par a fi rezumate rezonabil.

Acest sample arată diversitatea recenziilor pe care le găsim de obicei online, variind de la pozitive la negative (și totul între ele!). Deși exemplul cu titlul "meh" nu este foarte informativ, celelalte titluri par a fi rezumate decente ale recenziilor în sine. Antrenarea unui model de rezumare pe toate cele 400 000 de recenzii ar dura mult prea mult pe un singur GPU, așa că ne vom concentra pe generarea de rezumate pentru un singur domeniu de produse. Pentru a avea o idee despre domeniile din care putem alege, să convertim `english_dataset` într-un `pandas.DataFrame` și să calculăm numărul de recenzii per categorie de produse:

```python
english_dataset.set_format("pandas")
english_df = english_dataset["train"][:]
# Show counts for top 20 products
english_df["product_category"].value_counts()[:20]
```

```python out
home                      17679
apparel                   15951
wireless                  15717
other                     13418
beauty                    12091
drugstore                 11730
kitchen                   10382
toy                        8745
sports                     8277
automotive                 7506
lawn_and_garden            7327
home_improvement           7136
pet_products               7082
digital_ebook_purchase     6749
pc                         6401
electronics                6186
office_product             5521
shoes                      5197
grocery                    4730
book                       3756
Name: product_category, dtype: int64
```

Cele mai populare produse din datasetul în limba engleză sunt despre articole de uz casnic, îmbrăcăminte și electronice fără fir. Cu toate acestea, pentru a rămâne la Amazontheme, să ne concentrăm pe rezumatul recenziilor de cărți - la urma urmei, acesta este motivul pentru care compania a fost fondată! Putem vedea două categorii de produse care se potrivesc (`book` și `digital_ebook_purchase`), deci să filtrăm dataseturile în ambele limbi doar pentru aceste produse. După cum am văzut în [Capitolul 5](/course/chapter5), funcția `Dataset.filter()` ne permite să tăiem un dataset foarte eficient, deci putem defini o funcție simplă pentru a face acest lucru:

```python
def filter_books(example):
    return (
        example["product_category"] == "book"
        or example["product_category"] == "digital_ebook_purchase"
    )
```

Acum, când aplicăm această funcție la `english_dataset` și `spanish_dataset`, rezultatul va conține doar acele rânduri care implică categoriile de cărți. Înainte de a aplica filtrul, să schimbăm formatul din `english_dataset` din `"pandas"` înapoi în `"arrow"`:

```python
english_dataset.reset_format()
```

Putem aplica apoi funcția de filtrare și, ca o verificare a corectitudinii, să inspectăm un sample de recenzii pentru a vedea dacă acestea sunt într-adevăr despre cărți:

```python
spanish_books = spanish_dataset.filter(filter_books)
english_books = english_dataset.filter(filter_books)
show_samples(english_books)
```

```python out
'>> Title: I\'m dissapointed.'
'>> Review: I guess I had higher expectations for this book from the reviews. I really thought I\'d at least like it. The plot idea was great. I loved Ash but, it just didnt go anywhere. Most of the book was about their radio show and talking to callers. I wanted the author to dig deeper so we could really get to know the characters. All we know about Grace is that she is attractive looking, Latino and is kind of a brat. I\'m dissapointed.'

'>> Title: Good art, good price, poor design'
'>> Review: I had gotten the DC Vintage calendar the past two years, but it was on backorder forever this year and I saw they had shrunk the dimensions for no good reason. This one has good art choices but the design has the fold going through the picture, so it\'s less aesthetically pleasing, especially if you want to keep a picture to hang. For the price, a good calendar'

'>> Title: Helpful'
'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
```

Bine, putem vedea că recenziile nu sunt strict despre cărți și se pot referi la lucruri precum calendare și aplicații electronice precum OneNote. Cu toate acestea, domeniul pare potrivit pentru a antrena un model de sumarizare. Înainte de a analiza diferitele modele care sunt potrivite pentru această sarcină, trebuie să mai pregătim puțin datele: să combinăm recenziile în engleză și spaniolă ca un singur obiect `DatasetDict`. 🤗 Datasets oferă o funcție utilă `concatenate_datasets()` care (după cum sugerează și numele) va concatena două obiecte `Dataset` unul peste celălalt. Așadar, pentru a crea datasetul nostru bilingv, vom parcurge în buclă fiecare împărțire, vom concatena dataseturile pentru acel split și vom amesteca rezultatul pentru a ne asigura că modelul nostru nu se adaptează excesiv la o singură limbă:

```python
from datasets import concatenate_datasets, DatasetDict

books_dataset = DatasetDict()

for split in english_books.keys():
    books_dataset[split] = concatenate_datasets(
        [english_books[split], spanish_books[split]]
    )
    books_dataset[split] = books_dataset[split].shuffle(seed=42)

# Peek at a few examples
show_samples(books_dataset)
```

```python out
'>> Title: Easy to follow!!!!'
'>> Review: I loved The dash diet weight loss Solution. Never hungry. I would recommend this diet. Also the menus are well rounded. Try it. Has lots of the information need thanks.'

'>> Title: PARCIALMENTE DAÑADO'
'>> Review: Me llegó el día que tocaba, junto a otros libros que pedí, pero la caja llegó en mal estado lo cual dañó las esquinas de los libros porque venían sin protección (forro).'

'>> Title: no lo he podido descargar'
'>> Review: igual que el anterior'
```

Acest lucru arată cu siguranță ca un amestec de recenzii în engleză și spaniolă! Acum că avem un corpus de antrenament, un ultim lucru de verificat este distribuția cuvintelor în recenzii și în titlurile acestora. Acest lucru este deosebit de important pentru sarcinile de sumarizare, în cazul în care rezumatele scurte de referință din date pot influența modelul să producă doar unul sau două cuvinte în rezumatele generate. Graficele de mai jos arată distribuția cuvintelor și putem observa că titlurile sunt puternic înclinate spre 1-2 cuvinte:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/review-lengths.svg" alt="Distribuția numărului de cuvinte pentru titlurile și textele recenziei."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/review-lengths-dark.svg" alt="Distribuția numărului de cuvinte pentru titlurile și textele recenziei."/>
</div>

Pentru a rezolva acest lucru, vom filtra exemplele cu titluri foarte scurte, astfel încât modelul nostru să poată produce rezumate mai interesante. Deoarece avem de-a face cu texte în engleză și spaniolă, putem folosi rough heuristic pentru a face split titlurilor pe baza spațiului alb și apoi să folosim metoda noastră de încredere `Dataset.filter()` după cum urmează:

```python
books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
```

Now that we've prepared our corpus, let's take a look at a few possible Transformer models that one might fine-tune on it!

## Modele pentru sumarizarea textului[[models-for-text-summarization]]

Dacă vă gândiți bine, rezumarea textului este o sarcină similară cu machine translation: avem un corp de text, cum ar fi o recenzie, pe care am dori să o "traducem" într-o versiune mai scurtă care să capteze caracteristicile principale ale datelor de intrare. În consecință, majoritatea modelelor Transformer pentru rezumare adoptă arhitectura codificator-decodificator pe care am întâlnit-o pentru prima dată în [Capitolul 1](/course/chapter1), deși există unele excepții, cum ar fi familia de modele GPT, care poate fi, de asemenea, utilizată pentru rezumare în few-shot settings. Tabelul de mai jos enumeră câteva modele preantrenate populare care pot fi ajustate pentru sumarizare.

| Transformer model | Description                                                                                                                                                                                                    | Multilingual? |
| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
|    [GPT-2](https://huggingface.co/gpt2-xl)    | Deși este antrenat ca un model lingvistic autoregresiv, puteți face GPT-2 să genereze rezumate prin adăugarea "TL;DR" la sfârșitul textului de intrare.                                                                          |      ❌       |
|   [PEGASUS](https://huggingface.co/google/pegasus-large)   | Utilizează un obiectiv de preantrenare pentru a prezice propoziții mascate în texte cu mai multe propoziții. Acest obiectiv de preantrenare este mai apropiat de rezumare decât de vanilla language modeling și obține scoruri ridicate la standardele populare. |      ❌       |
|     [T5](https://huggingface.co/t5-base)      | O arhitectură Transformer universală care formulează toate sarcinile într-un framework text-text; de exemplu, formatul de intrare pentru modelul de rezumare a unui document este `summarize: ARTICOL`.                              |      ❌       |
|     [mT5](https://huggingface.co/google/mt5-base)     | O versiune multilingvă a T5, preantrenată pe corpusul multilingv Common Crawl (mC4), care acoperă 101 limbi.                                                                                                |      ✅       |
|    [BART](https://huggingface.co/facebook/bart-base)     | O nouă arhitectură Transformer cu un encoder și un stack de decodere antrenate pentru a reconstrui intrarea coruptă care combină schemele de preantrenare ale BERT și GPT-2.                                   |      ❌       |
|  [mBART-50](https://huggingface.co/facebook/mbart-large-50)   | O versiune multilingvă a BART, preantrenată pe 50 de limbi.                                                                                                                                                     |      ✅       |

După cum puteți vedea din acest tabel, majoritatea modelelor Transformer pentru rezumare (și, într-adevăr, majoritatea sarcinilor NLP) sunt monolingve. Acest lucru este grozav dacă sarcina voastrăeste într-o limbă cu  multe resurse precum engleza sau germana, dar mai puțin pentru miile de alte limbi utilizate în întreaga lume. Din fericire, există o clasă de modele Transformer multilingve, precum mT5 și mBART, care vin în ajutor. Aceste modele sunt preantrenate folosind modelarea limbajului, dar cu o întorsătură: în loc să fie antrenate pe un corpus dintr-o singură limbă, ele sunt antrenate împreună pe texte în peste 50 de limbi deodată!

Ne vom concentra asupra mT5, o arhitectură interesantă bazată pe T5, care a fost preantrenată într-un cadru text-to-text. În T5, fiecare sarcină NLP este formulată în termenii unui prompt prefix precum `summarize:` care condiționează modelul să adapteze textul generat la prompt. După cum se arată în figura de mai jos, acest lucru face T5 extrem de versatil, deoarece puteți rezolva multe sarcini cu un singur model!

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/t5.svg" alt="Diferite sarcini îndeplinite de arhitectura T5."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/t5-dark.svg" alt="Diferite sarcini îndeplinite de arhitectura T5."/>
</div>

mT5 nu utilizează prefixe, dar împărtășește o mare parte din versatilitatea T5 și are avantajul de a fi multilingv. Acum că am ales un model, să aruncăm o privire la pregătirea datelor noastre pentru antrenare.


> [!TIP]
> ✏️ **Încercați!** După ce ați parcurs această secțiune, vedeți cât de bine se compară mT5 cu mBART prin aplicarea fine-tuningului acestuia din urmă cu aceleași tehnici. Pentru puncte bonus, puteți încerca, de asemenea, fine-tuningul a T5 doar pe recenziile în limba engleză. Deoarece T5 are un prefix prompt special, va trebui să adăugați `summarize:` la exemplele de intrare în pașii de preprocesare de mai jos.

## Preprocessing the data[[preprocessing-the-data]]

<Youtube id="1m7BerpSq8A"/>

Următoarea noastră sarcină este să tokenizăm și să codificăm recenziile și titlurile acestora. Ca de obicei, începem prin încărcarea tokenizelui asociat cu checkpointul modelului preantrenat. Vom folosi `mt5-small` ca checkpoint, astfel încât să putem face fine-tune modelului într-un timp rezonabil:

```python
from transformers import AutoTokenizer

model_checkpoint = "google/mt5-small"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

> [!TIP]
> 💡 În stadiile inițiale ale proiectelor NLP, o bună practică este de a antrena o clasă de modele "mici" pe un sample mic de date. Acest lucru vă permite să faceți debug și să iterați mai rapid către un flux de lucru end-to-end. Odată ce sunteți încrezător în rezultate, puteți oricând să măriți modelul prin simpla schimbare a checkpointului modelului!

Să testăm tokenizerul mT5 pe un mic exemplu:

```python
inputs = tokenizer("I loved reading the Hunger Games!")
inputs
```

```python out
{'input_ids': [336, 259, 28387, 11807, 287, 62893, 295, 12507, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
```

Aici putem vedea binecunoscutele `input_ids` și `attention_mask` pe care le-am întâlnit în primele noastre experimente de fine-tuning în [Capitolul 3](/course/chapter3). Să decodificăm aceste ID-uri de intrare cu funcția `convert_ids_to_tokens()` a tokenizerului pentru a vedea cu ce fel de tokenizer avem de-a face:

```python
tokenizer.convert_ids_to_tokens(inputs.input_ids)
```

```python out
['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '</s>']
```

Caracterul Unicode special `▁` și tokenul de sfârșit de secvență `</s>` indică faptul că avem de-a face cu tokenizeerul SentencePiece, care se bazează pe algoritmul de segmentare Unigram discutat în [Capitolul 6](/course/chapter6). Unigram este deosebit de util pentru corpusurile multilingve, deoarece permite SentencePiece să fie agnostic în ceea ce privește accentele, punctuația și faptul că multe limbi, precum japoneza, nu au caractere de spațiu alb.

Pentru a tokeniza corpusul nostru, trebuie să ne ocupăm de o subtilitate asociată cu rezumarea: deoarece labelurile noastre sunt, de asemenea, text, este posibil ca acestea să depășească dimensiunea maximă a contextului modelului. Acest lucru înseamnă că trebuie să aplicăm trunchierea atât a recenziilor, cât și a titlurilor acestora, pentru a ne asigura că nu trecem inputuri excesiv de lungi modelului nostru. Tokenizerele din 🤗 Transformers oferă un argument ingenios, `text_target`,  care vă permite să tokenizați labelurile în paralel cu inputurile. Iată un exemplu al modului în care inputurile și targeturile sunt procesate pentru mT5:

```python
max_input_length = 512
max_target_length = 30


def preprocess_function(examples):
    model_inputs = tokenizer(
        examples["review_body"],
        max_length=max_input_length,
        truncation=True,
    )
    labels = tokenizer(
        examples["review_title"], max_length=max_target_length, truncation=True
    )
    model_inputs["labels"] = labels["input_ids"]
    return model_inputs
```

Să parcurgem acest cod pentru a înțelege ce se întâmplă. Primul lucru pe care l-am făcut a fost să definim valorile pentru `max_input_length` și `max_target_length`, care stabilesc limitele superioare pentru cât de lungi pot fi recenziile și titlurile noastre. Deoarece corpul recenziei este de obicei mult mai mare decât titlul, am mărit aceste valori în consecință.

Cu ajutorul funcției `preprocess_function()`, este simplu să tokenizăm întregul corpus cu ajutorul funcției practice `Dataset.map()` pe care am folosit-o la greu pe parcursul acestui curs:

```python
tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
```

Acum că corpusul a fost preprocesat, să aruncăm o privire asupra unor metrici care sunt utilizate în mod obișnuit pentru sumarizare. După cum vom vedea, nu există un glonț de argint atunci când vine vorba de măsurarea calității textului generat de calculator.

> [!TIP]
> 💡 Poate ați observat că am folosit `batched=True` în funcția noastră `Dataset.map()` de mai sus. Aceasta codifică exemplele în batchuri de 1.000 (implicit) și vă permite să utilizați capacitățile multithreading ale tokenizerilor rapizi din 🤗 Transformers. Atunci când este posibil, încercați să utilizați `batched=True` pentru a profita la maximum de preprocesare!


## Metrice pentru sumarizare[[metrics-for-text-summarization]]

<Youtube id="TMshhnrEXlg"/>

În comparație cu majoritatea celorlalte sarcini pe care le-am abordat în acest curs, măsurarea performanței sarcinilor de generare a textului, precum sumarizare sau traducerea, nu este la fel de simplă. De exemplu, având în vedere o recenzie precum "Mi-a plăcut să citesc Hunger Games", există mai multe rezumate valide, precum "Mi-a plăcut Hunger Games" sau "Hunger Games este o lectură excelentă". În mod clar, aplicarea unui exact match între rezumatul generat și label nu este o soluție bună - chiar și oamenii s-ar descurca prost cu un astfel de metric, deoarece toți avem propriul nostru stil de scriere.

Pentru rezumare, una dintre cele mai frecvent utilizate metrici este [ROUGE score](https://en.wikipedia.org/wiki/ROUGE_(metric)) (prescurtarea de la Recall-Oriented Understudy for Gisting Evaluation). Ideea de bază din spatele acestei metrici este de a compara un rezumat generat cu un set de rezumate de referință care sunt de obicei create de oameni. Pentru a face acest lucru mai precis, să presupunem că dorim să comparăm următoarele două rezumate:

```python
generated_summary = "I absolutely loved reading the Hunger Games"
reference_summary = "I loved reading the Hunger Games"
```

O modalitate de a le compara ar fi să numărați numărul de cuvinte care se suprapun, care în acest caz ar fi 6. Cu toate acestea, acest lucru este un pic crud, astfel încât, în schimb, ROUGE se bazează pe calcularea scorurilor _preicision_ și _recall_ pentru suprapunere.

> [!TIP]
> 🙋 Nu vă faceți griji dacă aceasta este prima dată când auziți de precision și recall - vom trece împreună prin câteva exemple explicite pentru a clarifica totul. Aceste metrici sunt de obicei întâlnite în sarcinile de clasificare, deci dacă doriți să înțelegeți cum sunt definite precizia și recallul în acest context, vă recomandăm să consultați [ghidurile `scikit-learn`] (https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).

Pentru ROUGE, recall măsoară cât de mult din rezumatul de referință este capturat de cel generat. Dacă comparăm doar cuvinte, recall poate fi calculată conform următoarei formule:

$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$

Pentru exemplul nostru simplu de mai sus, această formulă oferă un recall perfect de 6/6 = 1; adică, toate cuvintele din rezumatul de referință au fost produse de model. Acest lucru poate părea grozav, dar imaginați-vă dacă rezumatul nostru generat ar fi fost "Mi-a plăcut foarte mult să citesc Jocurile Foamei toată noaptea". Aceasta ar avea, de asemenea, o reamintire perfectă, dar este, fără îndoială, un rezumat mai prost, deoarece are mai multe cuvinte. Pentru a face față acestor scenarii, calculăm și precizia, care, în contextul ROUGE, măsoară cât de mult din rezumatul generat a fost relevant:


$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$

Aplicând acest lucru la rezumatul nostru cu mai multe cuvinte, se obține o precizie de 6/10 = 0,6, ceea ce este considerabil mai rău decât precizia de 6/7 = 0,86 obținută de rezumatul nostru mai scurt. În practică, se calculează de obicei atât precision, cât și recallul, iar apoi se raportează scorul F1 (media armonică a precision și recall). Putem face acest lucru cu ușurință în 🤗 Datasets instalând mai întâi biblioteca `rouge_score`:

```py
!pip install rouge_score
```

și apoi încărcând metrica ROUGE după cum urmează:

```python
import evaluate

rouge_score = evaluate.load("rouge")
```

Apoi, putem utiliza funcția `rouge_score.compute()` pentru a calcula toate metricile odată:

```python
scores = rouge_score.compute(
    predictions=[generated_summary], references=[reference_summary]
)
scores
```

```python out
{'rouge1': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
 'rouge2': AggregateScore(low=Score(precision=0.67, recall=0.8, fmeasure=0.73), mid=Score(precision=0.67, recall=0.8, fmeasure=0.73), high=Score(precision=0.67, recall=0.8, fmeasure=0.73)),
 'rougeL': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
 'rougeLsum': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92))}
```

Whoa, există o mulțime de informații în aceast output - ce înseamnă toate acestea? În primul rând, 🤗 Datasets calculează de fapt confidence intervalurile pentru precision, recall și scorul F1; acestea sunt atributele `low`, `mid` și `high` pe care le puteți vedea aici. În plus, 🤗 Datasets calculează o varietate de scoruri ROUGE care se bazează pe diferite tipuri de granularitate a textului atunci când compară rezumatele generate și de referință. Varianta `rouge1` este suprapunerea unigramelor - acesta este doar un mod elegant de a spune suprapunerea cuvintelor și este exact metrica pe care am discutat-o mai sus. Pentru a verifica acest lucru, să extragem valoarea `mid` a scorurilor noastre:

```python
scores["rouge1"].mid
```

```python out
Score(precision=0.86, recall=1.0, fmeasure=0.92)
```

Grozav, numerele de precision și de recall se potrivesc! Acum ce se întâmplă cu celelalte scoruri ROUGE? `rouge2` măsoară suprapunerea dintre bigrame (suprapunerea perechilor de cuvinte), în timp ce `rougeL` și `rougeLsum` măsoară cele mai lungi secvențe de cuvinte care se potrivesc, căutând cele mai lungi substraturi comune în rezumatele generate și de referință. Termenul "sum" din `rougeLsum` se referă la faptul că această metrică este calculată pentru un rezumat întreg, în timp ce `rougeL` este calculată ca medie a propozițiilor individuale.

> [!TIP]
> ✏️ **Încercați!** Creați propriul exemplu de rezumat generat și de referință și vedeți dacă scorurile ROUGE rezultate sunt în concordanță cu un calcul manual bazat pe formulele de precision și recall. Pentru puncte bonus, împărțiți textul în bigrame și comparați precizia și recallul pentru metrica `rouge2`.

Vom folosi aceste scoruri ROUGE pentru a urmări performanța modelului nostru, dar înainte de a face acest lucru, să facem ceva ce orice bun practician NLP ar trebui să facă: să creăm un baseline puternic, dar simplu!

### Crearea unui baseline bun[[creating-a-strong-baseline]]

un baseline obișnuit pentru rezumarea textului este de a lua pur și simplu primele trei propoziții ale unui articol, adesea numit _lead-3_ baseline. Am putea folosi puncte de oprire pentru a urmări limitele propoziției, dar acest lucru va eșua în cazul acronimelor precum "U.S." sau "U.N." - așa că vom folosi în schimb biblioteca `nltk`, care include un algoritm mai bun pentru a gestiona aceste cazuri. Puteți instala pachetul folosind `pip` după cum urmează:

```python
!pip install nltk
```

și apoi descărcați regulile de punctuație:

```python
import nltk

nltk.download("punkt")
```

În continuare, importăm tokenizerul de propoziții din `nltk` și creăm o funcție simplă pentru a extrage primele trei propoziții dintr-o recenzie. Convenția în rezumarea textului este de a separa fiecare rezumat cu o linie nouă, deci să includem și aceasta și să o testăm pe un exemplu de antrenare:

```python
from nltk.tokenize import sent_tokenize


def three_sentence_summary(text):
    return "\n".join(sent_tokenize(text)[:3])


print(three_sentence_summary(books_dataset["train"][1]["review_body"]))
```

```python out
'I grew up reading Koontz, and years ago, I stopped,convinced i had "outgrown" him.'
'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.'
'She found Strangers.'
```

Acest lucru pare să funcționeze, deci să implementăm acum o funcție care extrage aceste "rezumate" dintr-un dataset și calculează scorurile ROUGE pentru baseline:

```python
def evaluate_baseline(dataset, metric):
    summaries = [three_sentence_summary(text) for text in dataset["review_body"]]
    return metric.compute(predictions=summaries, references=dataset["review_title"])
```

Putem folosi apoi această funcție pentru a calcula scorurile ROUGE pe setul de validare și pentru a le înfrumuseța puțin folosind Pandas:

```python
import pandas as pd

score = evaluate_baseline(books_dataset["validation"], rouge_score)
rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
rouge_dict = dict((rn, round(score[rn].mid.fmeasure * 100, 2)) for rn in rouge_names)
rouge_dict
```

```python out
{'rouge1': 16.74, 'rouge2': 8.83, 'rougeL': 15.6, 'rougeLsum': 15.96}
```

Putem vedea că scorul `rouge2` este semnificativ mai mic decât restul; acest lucru reflectă probabil faptul că titlurile recenziilor sunt de obicei concise și, prin urmare, baselineul lead-3 are prea multe cuvinte. Acum, că avem un baseline bun de lucru, să ne îndreptăm atenția către fine-tuningul mT5!

{#if fw === 'pt'}

## Fine-tuningul mT5 cu API-ul `Trainer`[[fine-tuning-mt5-with-the-trainer-api]]

Fine-tuningul unui model pentru rezumare este foarte asemănător cu celelalte sarcini pe care le-am acoperit în acest capitol. Primul lucru pe care trebuie să îl facem este să încărcăm modelul preantrenat din checkpointul `mt5-small`. Deoarece sumarizarea este o sarcină de la secvență la secvență, putem încărca modelul cu clasa `AutoModelForSeq2SeqLM`, care va descărca automat și va stoca în cache weighturile:

```python
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Fine-tuningul mT5 cu Keras[[fine-tuning-mt5-with-keras]]

Fine-tuningul unui model pentru sumarizare este foarte asemănătoare cu celelalte sarcini pe care le-am acoperit în acest capitol. Primul lucru pe care trebuie să îl facem este să încărcăm modelul preantrenat din punctul de control `mt5-small`. Deoarece rezumarea este o sarcină de la secvență la secvență, putem încărca modelul cu clasa `TFAutoModelForSeq2SeqLM`, care va descărca și va stoca în cache weighturile:

```python
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{/if}

> [!TIP]
> 💡 Dacă vă întrebați de ce nu vedeți niciun avertisment cu privire la fine-tuningul modelului pe un downstream task, acest lucru se datorează faptului că pentru sarcinile secvență-la-secvență păstrăm toate weighturile rețelei. Comparați acest lucru cu modelul nostru de clasificare a textului din [Capitolul 3](/course/chapter3), unde headul modelului preantrenat a fost înlocuit cu o rețea inițializată aleatoriu.

Următorul lucru pe care trebuie să îl facem este să ne conectăm la Hugging Face Hub. Dacă executați acest cod într-un notebook, puteți face acest lucru cu următoarea funcție:

```python
from huggingface_hub import notebook_login

notebook_login()
```

care va afișa un widget în care puteți introduce credențialele. Alternativ, puteți rula această comandă în terminal și să vă conectați acolo:

```
huggingface-cli login
```

{#if fw === 'pt'}

Va trebui să generăm rezumate pentru a calcula scorurile ROUGE în timpul antrenării. Din fericire, 🤗 Transformers oferă clase dedicate `Seq2SeqTrainingArguments` și `Seq2SeqTrainer` care pot face acest lucru pentru noi în mod automat! Pentru a vedea cum funcționează acest lucru, să definim mai întâi hiperparametrii și alte argumente pentru experimentele noastre:

```python
from transformers import Seq2SeqTrainingArguments

batch_size = 8
num_train_epochs = 8
# Show the training loss with every epoch
logging_steps = len(tokenized_datasets["train"]) // batch_size
model_name = model_checkpoint.split("/")[-1]

args = Seq2SeqTrainingArguments(
    output_dir=f"{model_name}-finetuned-amazon-en-es",
    evaluation_strategy="epoch",
    learning_rate=5.6e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=num_train_epochs,
    predict_with_generate=True,
    logging_steps=logging_steps,
    push_to_hub=True,
)
```

Aici, argumentul `predict_with_generate` a fost setat pentru a indica faptul că ar trebui să generăm rezumate în timpul evaluării, astfel încât să putem calcula scorurile ROUGE pentru fiecare epocă. După cum s-a discutat în [Capitolul 1](/course/chapter1), decodificatorul realizează inference-ul prin prezicerea tokenilor unul câte unul, iar acest lucru este implementat de metoda `generate()` a modelului. Setarea `predict_with_generate=True` îi spune lui `Seq2SeqTrainer` să utilizeze această metodă pentru evaluare. Am ajustat, de asemenea, unii dintre hiperparametrii impliciți, cum ar fi rata de învățare, numărul de epoci și scăderea weighturilor și am setat opțiunea `save_total_limit` pentru a salva numai până la 3 checkpointuri în timpul antrenamentului - acest lucru se datorează faptului că chiar și versiunea "mică" a mT5 utilizează aproximativ 1 GB de spațiu pe hard disk și putem economisi puțin spațiu prin limitarea numărului de copii salvate.

Argumentul `push_to_hub=True` ne va permite să trimitem modelul în Hub după antrenare; veți găsi repositoriul în profilul vostru de utilizator, în locația definită de `output_dir`. Rețineți că puteți specifica numele repositoriului către care doriți să trimiteți modelul cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a trimite modelul către o organizație). De exemplu, atunci când am trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"` la `Seq2SeqTrainingArguments`.

Următorul lucru pe care trebuie să îl facem este să oferim trainerului o funcție `compute_metrics()`, astfel încât să ne putem evalua modelul în timpul antrenării. Pentru sumarizare, acest lucru este un pic mai complicat decât simpla apelare a funcției `rouge_score.compute()` pentru predicțiile modelului, deoarece trebuie să _decodăm_ rezultatele și labelurile din text înainte de a putea calcula scorurile ROUGE. Următoarea funcție face exact acest lucru și, de asemenea, utilizează funcția `sent_tokenize()` din `nltk` pentru a separa propozițiile rezumate cu linii noi:

```python
import numpy as np


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # Decode generated summaries into text
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    # Replace -100 in the labels as we can't decode them
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    # Decode reference summaries into text
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    # ROUGE expects a newline after each sentence
    decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
    decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
    # Compute ROUGE scores
    result = rouge_score.compute(
        predictions=decoded_preds, references=decoded_labels, use_stemmer=True
    )
    # Extract the median scores
    result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
    return {k: round(v, 4) for k, v in result.items()}
```

{/if}

În continuare, trebuie să definim un data collator pentru sarcina noastră secvență-la-secvență. Deoarece mT5 este un transformer model encoder-decoder, o subtilitate a pregătirii batch-urilor noastre este că, în timpul decodificării, trebuie să deplasăm labelurile la dreapta cu una. Acest lucru este necesar pentru a ne asigura că decodificatorul vede doar labelurile anterioare ale adevărului de bază și nu pe cele actuale sau viitoare, care ar fi ușor de memorat de către model. Acest lucru este similar cu modul în care masked self-attention este aplicată inputurilor într-o sarcină precum [causal language modeling](/course/chapter7/6).

Din fericire, 🤗 Transformers oferă un collator `DataCollatorForSeq2Seq` care va face padding dinamic inputurilor și labelurilor pentru noi. Pentru a inițializa acest collator, trebuie doar să furnizăm `tokenizer` și `model`:

{#if fw === 'pt'}

```python
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```python
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Să vedem ce produce acest collator atunci când este alimentat cu un mic batch de exemple. În primul rând, trebuie să eliminăm coloanele cu șiruri de caractere, deoarece collatorul nu va ști cum să completeze aceste elemente:

```python
tokenized_datasets = tokenized_datasets.remove_columns(
    books_dataset["train"].column_names
)
```

Deoarece collatorul așteaptă o listă de `dict`, unde fiecare `dict` reprezintă un singur exemplu din dataset, trebuie să transformăm datele în formatul așteptat înainte de a le transmite data collatorului:

```python
features = [tokenized_datasets["train"][i] for i in range(2)]
data_collator(features)
```

```python out
{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[  1494,    259,   8622,    390,    259,    262,   2316,   3435,    955,
            772,    281,    772,   1617,    263,    305,  14701,    260,   1385,
           3031,    259,  24146,    332,   1037,    259,  43906,    305,    336,
            260,      1,      0,      0,      0,      0,      0,      0],
        [   259,  27531,  13483,    259,   7505,    260, 112240,  15192,    305,
          53198,    276,    259,  74060,    263,    260,    459,  25640,    776,
           2119,    336,    259,   2220,    259,  18896,    288,   4906,    288,
           1037,   3931,    260,   7083, 101476,   1143,    260,      1]]), 'labels': tensor([[ 7483,   259,  2364, 15695,     1,  -100],
        [  259, 27531, 13483,   259,  7505,     1]]), 'decoder_input_ids': tensor([[    0,  7483,   259,  2364, 15695,     1],
        [    0,   259, 27531, 13483,   259,  7505]])}
```

Principalul lucru care trebuie observat aici este că primul exemplu este mai lung decât al doilea, astfel încât `input_ids` și `attention_mask` din al doilea exemplu au primit padding în dreapta cu un simbol `[PAD]` (al cărui ID este `0`). În mod similar, putem vedea că `labels` au primit padding cu `-100`, pentru a ne asigura că tokenii de padding sunt ignorați de funcția de pierdere. În sfârșit, putem vedea un nou `decoder_input_ids` care a deplasat labelurile spre dreapta prin inserarea unui simbol `[PAD]` în prima intrare.

{#if fw === 'pt'}

Avem în sfârșit toate ingredientele de care avem nevoie pentru a antrenare! Acum trebuie doar să inițializăm trainerul cu argumentele standard:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

și să lansăm cursa noastră de antrenare:

```python
trainer.train()
```

În timpul antrenamentului, ar trebui să vedeți cum training loss scade și scorurile ROUGE cresc cu fiecare epocă. După ce antrenamentul este complet, puteți vedea scorurile ROUGE finale executând `Trainer.evaluate()`:

```python
trainer.evaluate()
```

```python out
{'eval_loss': 3.028524398803711,
 'eval_rouge1': 16.9728,
 'eval_rouge2': 8.2969,
 'eval_rougeL': 16.8366,
 'eval_rougeLsum': 16.851,
 'eval_gen_len': 10.1597,
 'eval_runtime': 6.1054,
 'eval_samples_per_second': 38.982,
 'eval_steps_per_second': 4.914}
```

Din scorurile obținute, putem vedea că modelul nostru a depășit cu mult modelul nostru de bază lead-3 - frumos! Ultimul lucru de făcut este să introducem weighturile modelului în Hub, după cum urmează:

```
trainer.push_to_hub(commit_message="Training complete", tags="summarization")
```

```python out
'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
```

Acest lucru va salva checkpointul și fișierele de configurare în `output_dir`, înainte de a încărca toate fișierele în Hub. Specificând argumentul `tags`, ne asigurăm, de asemenea, că widgetul de pe Hub va fi unul pentru un pipeline de rezumare în locul celui implicit de generare de text asociat arhitecturii mT5 (pentru mai multe informații despre labelurile modelului, consultați [🤗Documentația Hub](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined)). Rezultatul din `trainer.push_to_hub()` este o adresă URL către hash-ul Git commit, astfel încât să puteți vedea cu ușurință modificările care au fost făcute în repositoriul modelului!

Pentru a încheia această secțiune, să aruncăm o privire la modul în care putem, de asemenea, să facem fine-tune la mT5 folosind featururile de nivel scăzut oferite de 🤗 Accelerate.

{:else}

Suntem aproape gata de antrenament! Trebuie doar să convertim dataseturile în `tf.data.Dataset`s folosind data collatorul pe care l-am definit mai sus, iar apoi aplicarea `compil()` și `fit()` modelul. Mai întâi, dataseturile:

```python
tf_train_dataset = model.prepare_tf_dataset(
    tokenized_datasets["train"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=8,
)
tf_eval_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=8,
)
```

Acum, ne definim hiperparametrii de antrenare și compilare:

```python
from transformers import create_optimizer
import tensorflow as tf

# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied
# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset,
# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size.
num_train_epochs = 8
num_train_steps = len(tf_train_dataset) * num_train_epochs
model_name = model_checkpoint.split("/")[-1]

optimizer, schedule = create_optimizer(
    init_lr=5.6e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)

model.compile(optimizer=optimizer)

# Train in mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

În cele din urmă, facem fit modelului modelului. Folosim un `PushToHubCallback` pentru a salva modelul în Hub după fiecare epocă, ceea ce ne va permite să îl folosim ulterior pentru inference:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir=f"{model_name}-finetuned-amazon-en-es", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback], epochs=8
)
```

Am obținut unele valori ale pierderilor în timpul antrenării, dar am dori să vedem metricile ROUGE pe care le-am calculat mai devreme. Pentru a obține aceste metrici, va trebui să generăm outputuri din model și să le convertim în șiruri de caractere. Să construim câteva liste de labels și predicții pentru a compara metrica ROUGE (rețineți că, dacă obțineți erori de import pentru această secțiune, este posibil să fie necesar să faceți `!pip install tqdm`). De asemenea, vom utiliza un truc care crește dramatic performanța - compilarea codului nostru de generare cu [XLA](https://www.tensorflow.org/xla), compilatorul accelerat de algebră liniară al TensorFlow. XLA aplică diverse optimizări graficului de calcul al modelului și are ca rezultat îmbunătățiri semnificative ale vitezei și utilizării memoriei. După cum se descrie în [blogul Hugging Face](https://huggingface.co/blog/tf-xla-generate), XLA funcționează cel mai bine atunci când formele noastre de input nu variază prea mult. Pentru a face față acestui lucru, vom face padding inputurilor la multipli a 128 și vom crea un nou dataset cu padding collatorul, iar apoi vom aplica decoratorul `@tf.function(jit_compile=True)` funcției noastre de generare, care marchează întreaga funcție pentru compilare cu XLA.

```python
from tqdm import tqdm
import numpy as np

generation_data_collator = DataCollatorForSeq2Seq(
    tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=320
)

tf_generate_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=generation_data_collator,
    shuffle=False,
    batch_size=8,
    drop_remainder=True,
)


@tf.function(jit_compile=True)
def generate_with_xla(batch):
    return model.generate(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        max_new_tokens=32,
    )


all_preds = []
all_labels = []
for batch, labels in tqdm(tf_generate_dataset):
    predictions = generate_with_xla(batch)
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    labels = labels.numpy()
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
    decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
    all_preds.extend(decoded_preds)
    all_labels.extend(decoded_labels)
```

Odată ce avem listele noastre de labeluri și de șiruri de predicție, calcularea scorului ROUGE este ușoară:

```python
result = rouge_score.compute(
    predictions=decoded_preds, references=decoded_labels, use_stemmer=True
)
result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
{k: round(v, 4) for k, v in result.items()}
```

```
{'rouge1': 31.4815, 'rouge2': 25.4386, 'rougeL': 31.4815, 'rougeLsum': 31.4815}
```


{/if}

{#if fw === 'pt'}

## Fine-tuningul mT5 cu 🤗 Accelerate[[fine-tuning-mt5-with-accelerate]]

Fine-tuningul nostru cu 🤗 Accelerate este foarte asemănător cu exemplul de clasificare a textului pe care l-am întâlnit în [Capitolul 3](/course/chapter3). Principalele diferențe vor fi necesitatea de a genera în mod explicit rezumatele noastre în timpul antrenării și de a defini modul în care calculăm scorurile ROUGE (reamintim că `Seq2SeqTrainer` a avut grijă de generare pentru noi). Să aruncăm o privire la modul în care putem implementa aceste două cerințe în cadrul 🤗 Accelerate!

### Pregătirea pentru antrenare[[preparing-everything-for-training]]

Primul lucru pe care trebuie să-l facem este să creăm un `DataLoader` pentru fiecare dintre spliturile noastre. Deoarece dataloaders PyTorch așteaptă batchuri de tensori, trebuie să setăm formatul la `"torch"` în dataseturile noastre:

```python
tokenized_datasets.set_format("torch")
```

Acum că avem dataseturi formate doar din tensori, următorul lucru este inițializarea `DataCollatorForSeq2Seq`. Pentru aceasta trebuie să furnizăm o versiune nouă a modelului, așa că hai să îl încărcăm din nou din cache:

```python
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Putem apoi să instanțiem data collatorul și să îl folosim pentru a ne defini dataloaders:

```python
from torch.utils.data import DataLoader

batch_size = 8
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=batch_size,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=batch_size
)
```

Următorul lucru este definirea optimizatorului pe care dorim să îl utilizăm. Ca și în celelalte exemple, vom folosi `AdamW`, care funcționează bine pentru majoritatea problemelor:

```python
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

În cele din urmă, introducem modelul, optimizatorul și dataloaders în metoda `accelerator.prepare()`:

```python
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

> [!TIP]
> 🚨 Dacă faceți antrenarea pe un TPU, va trebui să mutați tot codul de mai sus într-o funcție de antrenare aparte. Consultați [Capitolul 3](/course/chapter3) pentru mai multe detalii.

Acum că ne-am pregătit obiectele, mai avem trei lucruri de făcut:

* Definirea learning rate schedule.
* Implementarea unei funcții de post-procesare a rezumatelor pentru evaluare.
* Crearea unui repositoriu pe Hub în care să putem trimite modelul nostru.

Pentru learning rate schedule, îl vom utiliza pe cel liniar standard din secțiunile anterioare:

```python
from transformers import get_scheduler

num_train_epochs = 10
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Pentru post-procesare, avem nevoie de o funcție care să împartă rezumatele generate în propoziții separate prin linii noi. Acesta este formatul pe care îl așteaptă metrica ROUGE, iar noi putem realiza acest lucru cu următorul fragment de cod:

```python
def postprocess_text(preds, labels):
    preds = [pred.strip() for pred in preds]
    labels = [label.strip() for label in labels]

    # ROUGE expects a newline after each sentence
    preds = ["\n".join(nltk.sent_tokenize(pred)) for pred in preds]
    labels = ["\n".join(nltk.sent_tokenize(label)) for label in labels]

    return preds, labels
```

Acest lucru ar trebui să vă pară familiar dacă vă amintiți cum am definit funcția `compute_metrics()` a `Seq2SeqTrainer`.

În cele din urmă, trebuie să creăm un repositoriu de modele pe Hugging Face Hub. Pentru aceasta, putem utiliza biblioteca 🤗 Hub intitulată corespunzător . Trebuie doar să definim un nume pentru repositoriul nostru, iar biblioteca are o funcție utilitară pentru a combina ID-ul repositoriul cu profilul utilizatorului:

```python
from huggingface_hub import get_full_repo_name

model_name = "test-bert-finetuned-squad-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'lewtun/mt5-finetuned-amazon-en-es-accelerate'
```

Acum putem folosi numele repositoriului pentru a clona o versiune locală în folderul nostru cu rezultate care va stoca artefactele de antrenare:

```python
from huggingface_hub import Repository

output_dir = "results-mt5-finetuned-squad-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acest lucru ne va permite să trimitem artefactele înapoi la Hub prin apelarea metodei `repo.push_to_hub()` în timpul antrenării! Să încheiem acum analiza noastră prin scrierea buclei de antrenare.

### Bucla de antrenare[[training-loop]]

Bucla de formare pentru sumarizare este destul de asemănătoare cu celelalte exemple 🤗 Accelerate pe care le-am întâlnit și este împărțită aproximativ în patru etape principale:

1. Antrenarea modelului prin iterarea peste toate exemplele din `train_dataloader` pentru fiecare epocă.
2. Generarea rezumatelor modelului la sfârșitul fiecărei epoci, mai întâi prin generarea token-urilor și apoi prin decodarea lor (și a rezumatelor de referință) în text.
3. Calcularea scorurilor ROUGE folosind aceleași tehnici pe care le-am văzut mai devreme.
4. Salvați checkpointurile și încărcați totul pe Hub. Aici ne bazăm pe argumentul `blocking=False` al obiectului `Repository` astfel încât să putem împinge checkointurile pentru fiecare epocă _asincron_. Acest lucru ne permite să continuăm antrenamentul fără a fi nevoiți să așteptăm încărcarea oarecum lentă asociată cu un model de dimensiunea unui GB!

Acești pași pot fi observați în următorul bloc de cod:

```python
from tqdm.auto import tqdm
import torch
import numpy as np

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Training
    model.train()
    for step, batch in enumerate(train_dataloader):
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Evaluation
    model.eval()
    for step, batch in enumerate(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
            )

            generated_tokens = accelerator.pad_across_processes(
                generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
            )
            labels = batch["labels"]

            # If we did not pad to max length, we need to pad the labels too
            labels = accelerator.pad_across_processes(
                batch["labels"], dim=1, pad_index=tokenizer.pad_token_id
            )

            generated_tokens = accelerator.gather(generated_tokens).cpu().numpy()
            labels = accelerator.gather(labels).cpu().numpy()

            # Replace -100 in the labels as we can't decode them
            labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
            if isinstance(generated_tokens, tuple):
                generated_tokens = generated_tokens[0]
            decoded_preds = tokenizer.batch_decode(
                generated_tokens, skip_special_tokens=True
            )
            decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

            decoded_preds, decoded_labels = postprocess_text(
                decoded_preds, decoded_labels
            )

            rouge_score.add_batch(predictions=decoded_preds, references=decoded_labels)

    # Compute metrics
    result = rouge_score.compute()
    # Extract the median ROUGE scores
    result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
    result = {k: round(v, 4) for k, v in result.items()}
    print(f"Epoch {epoch}:", result)

    # Save and upload
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

```python out
Epoch 0: {'rouge1': 5.6351, 'rouge2': 1.1625, 'rougeL': 5.4866, 'rougeLsum': 5.5005}
Epoch 1: {'rouge1': 9.8646, 'rouge2': 3.4106, 'rougeL': 9.9439, 'rougeLsum': 9.9306}
Epoch 2: {'rouge1': 11.0872, 'rouge2': 3.3273, 'rougeL': 11.0508, 'rougeLsum': 10.9468}
Epoch 3: {'rouge1': 11.8587, 'rouge2': 4.8167, 'rougeL': 11.7986, 'rougeLsum': 11.7518}
Epoch 4: {'rouge1': 12.9842, 'rouge2': 5.5887, 'rougeL': 12.7546, 'rougeLsum': 12.7029}
Epoch 5: {'rouge1': 13.4628, 'rouge2': 6.4598, 'rougeL': 13.312, 'rougeLsum': 13.2913}
Epoch 6: {'rouge1': 12.9131, 'rouge2': 5.8914, 'rougeL': 12.6896, 'rougeLsum': 12.5701}
Epoch 7: {'rouge1': 13.3079, 'rouge2': 6.2994, 'rougeL': 13.1536, 'rougeLsum': 13.1194}
Epoch 8: {'rouge1': 13.96, 'rouge2': 6.5998, 'rougeL': 13.9123, 'rougeLsum': 13.7744}
Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 13.9509}
```

Și asta e tot! După ce executați acest lucru, veți avea un model și rezultate care sunt destul de asemănătoare cu cele obținute cu `Trainer`.

{/if}

## Utilizarea modelului fine-tuned[[using-your-fine-tuned-model]]

Odată ce ați încărcat modelul în Hub, vă puteți juca cu el prin widgetul de inference, fie cu un obiect `pipeline`, după cum urmează:

```python
from transformers import pipeline

hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
summarizer = pipeline("summarization", model=hub_model_id)
```

Putem introduce câteva exemple din setul de testare (pe care modelul nu le-a văzut) în pipelineul noastru pentru a avea o idee despre calitatea rezumatelor. Mai întâi, să implementăm o funcție simplă pentru a afișa împreună recenzia, titlul și rezumatul generat:

```python
def print_summary(idx):
    review = books_dataset["test"][idx]["review_body"]
    title = books_dataset["test"][idx]["review_title"]
    summary = summarizer(books_dataset["test"][idx]["review_body"])[0]["summary_text"]
    print(f"'>>> Review: {review}'")
    print(f"\n'>>> Title: {title}'")
    print(f"\n'>>> Summary: {summary}'")
```

Să aruncăm o privire la unul dintre exemplele englezești pe care le primim:

```python
print_summary(100)
```

```python out
'>>> Review: Nothing special at all about this product... the book is too small and stiff and hard to write in. The huge sticker on the back doesn’t come off and looks super tacky. I would not purchase this again. I could have just bought a journal from the dollar store and it would be basically the same thing. It’s also really expensive for what it is.'

'>>> Title: Not impressed at all... buy something else'

'>>> Summary: Nothing special at all about this product'
```

This is not too bad! We can see that our model has actually been able to perform _abstractive_ summarization by augmenting parts of the review with new words. And perhaps the coolest aspect of our model is that it is bilingual, so we can also generate summaries of Spanish reviews:

```python
print_summary(0)
```

```python out
'>>> Review: Es una trilogia que se hace muy facil de leer. Me ha gustado, no me esperaba el final para nada'

'>>> Title: Buena literatura para adolescentes'

'>>> Summary: Muy facil de leer'
```

Rezumatul se traduce prin "Very easy to read" în limba engleză, ceea ce putem vedea că în acest caz a fost extras direct din recenzie. Cu toate acestea, acest lucru arată versatilitatea modelului mT5 și v-a dat o idee despre cum este să aveți de-a face cu un corpus multilingv!

În continuare, ne vom îndrepta atenția către o sarcină puțin mai complexă: antrenarea unui model lingvistic de la zero.